package filecopier.console;

import java.io.IOException;
import java.io.File;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.BufferedReader;

import filecopier.Singleton;
import filecopier.ActionCommand;
import filecopier.NullAction;
import filecopier.FileCopierException;
import filecopier.ActionCancelException;
import filecopier.console.copy.CopyConsoleWorker;
import filecopier.console.restore.RestoreConsoleWorker;
import filecopier.options.Options;
import filecopier.script.Script;
import filecopier.script.CopyScript;
import filecopier.script.RestoreScript;

import custom.xml.DOMHelperException;

import org.apache.log4j.Logger;

public class FileCopier {

    private Script fromScript = null;
    private Script toScript = null;
    private Script restoreScript = null;
    private ActionCommand actionCommand = new NullAction();
    private Logger logger = null;

    public FileCopier() {
        Singleton.getInstance().getFileAlwaysLogger().info("FileCopier starting in console mode.");
        logger = Singleton.getInstance().getFileConsoleLogger();
        try {
            Options.ActionMode actionMode = initScripts();

            // See if user requested to view or change a script path.
            Options options = Singleton.getInstance().getOptions();
            Logger consoleLogger = Singleton.getInstance().getConsoleLogger();
            int ct = 0;
            // See if the user changed the copy path or script path. If he did, use the console logger.
            if (options.wasCopyScriptPathPathSet()) {
                consoleLogger.info("Copy Path = " + options.getCopyPath().getPath());
                ct++;
            }
            if (options.wasRestoreScriptPathSet()) {
                consoleLogger.info("Restore Path = " + options.getRestorePathWrapper().getRestorePath().getPath());
                ct++;
            }
            // See if the user requested a printout of the copy path or script path. If he did, use the console logger.
            if (options.printCopyScriptPath()) {
                consoleLogger.info("Copy Path = " + options.getCopyPath().getPath());
                ct++;
            }
            if (options.printRestoreScriptPath()) {
                consoleLogger.info("Restore Path = " + options.getRestorePathWrapper().getRestorePath().getPath());
                ct++;
            }
            // See if user requested a printout of the app version.
            if(options.printVersion()) {
                consoleLogger.info("FileCopier Version = " + Singleton.getInstance().getAppVersion());
                ct++;
            }
            
            if (actionMode == Options.ActionMode.Copy) {
                actionCommand = new CopyConsoleWorker(fromScript, toScript);
            } else if (actionMode == Options.ActionMode.Restore) {
                // Do restore command.
                actionCommand = new RestoreConsoleWorker(restoreScript);
            } else if(ct == 0) {
                // If ct is 0, user did not request to view the copy or restore script or change the copy or restore
                // script. Print out the usage text.
                if (ct == 0) {
                    usage();
                }
            }

            actionCommand.doAction();
        } catch (ActionCancelException e) {
            logger.warn("ActionCancelException encountered.", e);
        } catch (FileCopierException e) {
            logger.fatal("FileCopierException encountered", e);
        }

        Singleton.getInstance().getOptions().storeProperties();
    }

    private Options.ActionMode initScripts() throws FileCopierException {
        Options.ActionMode actionMode = Options.ActionMode.Unknown;

        Options options = Singleton.getInstance().getOptions();
        String fromScriptStr = options.getFromScriptStr();
        String toScriptStr = options.getToScriptStr();
        String restoreScriptStr = null;

        if (fromScriptStr != null && toScriptStr != null) {
            try {
                File fromFile = new File(fromScriptStr);
                File toFile = new File(toScriptStr);
                this.fromScript = new CopyScript(fromFile);
                this.toScript = new CopyScript(toFile);
                actionMode = Options.ActionMode.Copy;
            } catch (DOMHelperException e) {
                throw new FileCopierException(e, e);
            } catch (IOException e) {
                throw new FileCopierException(e, e);
            }
        } else if ((restoreScriptStr = options.getRestoreScriptStr()) != null) {
            try {
                File restoreFile = new File(restoreScriptStr);
                this.restoreScript = new RestoreScript(restoreFile);
                actionMode = Options.ActionMode.Restore;
            } catch (DOMHelperException e) {
                throw new FileCopierException(e);
            } catch (IOException e) {
                throw new FileCopierException(e);
            }
        }

        return actionMode;
    }

    private String usageBkup() {
        StringBuilder usageMsg = new StringBuilder("Usage: java -jar <path>/filecopier.jar -c -f <from script file> -t <to script path>");
        String newLine = System.getProperty("line.separator");
        usageMsg.append(newLine)
            .append("\tor: java -jar <path>filecopier.jar -c -r <resore script file>").append(newLine)
            .append("\tor: java -jar <path>filecopier.jar (No arguments opens filecopier in GUI mode)").append(newLine + newLine)
            .append("Arguments:").append(newLine).append("\t-a, --rename-fmt-string <format string>").append(newLine)
            .append("\t\tRename format string filecopier will use to rename files that already exist.").append(newLine)
            .append("\t\tIt can be a regular expression. The default is \"_%tY%tm%td_%tH%tM%tS%tL\".").append(newLine + newLine)
            .append("\t-c, --console").append(newLine)
            .append("\t\tOpen filecopier in console mode. Absence of this argument opens filecopier").append(newLine)
            .append("\t\tin GUI mode.").append(newLine + newLine)
            .append("\t-d, --dir-name-copy-filter <filter>").append(newLine)
            .append("\t\tA filter for directories. Directories whose name matches the filter will").append(newLine)
            .append("\t\tor will not be copied, depending on if it is inclusive or exclusive.").append(newLine + newLine)
            .append("\t-D, --dir-name-flatten-filter <filter>").append(newLine)
            .append("\t\tA filter for directories. Directories whose name matches the filter will").append(newLine)
            .append("\t\tor will not be flattened, depending on if it is inclusive or exclusive.").append(newLine + newLine)
            .append("\t-e, --file-exists [RenameAuto, RenameManual, Overwrite, Skip]").append(newLine)
            .append("\t\tAction FileCopier takes if a file already exists. \"RenameAuto\"").append(newLine)
            .append("\t\trenames the file according to the rename format string. \"RenameMaual\"").append(newLine)
            .append("\t\tprompts for a new name. \"Overwrite\" overwrites the existing file.").append(newLine)
            .append("\t\t\"Skip\" skips the file, taking no action. The default is \"RenameAuto\".").append(newLine + newLine)
            .append("\t-f, --from-script <from script file>").append(newLine)
            .append("\t\tThe copy script file that contains the full path of the files to copy. The").append(newLine)
            .append("\t\tname only of the from script file can be entered if the script file exists").append(newLine)
            .append("\t\tin the Copy Path. This argument is reguired in console mode (-c) to copy").append(newLine)
            .append("\t\tfiles.").append(newLine + newLine).append("\t-F, --max-dir-flatten-level <integer value>").append(newLine)
            .append("\t\tThe maximum level of directories to flatten. If the value is -1, no").append(newLine)
            .append("\t\tdirectories are flattened. If the value is 0, all directories are flattened.").append(newLine)
            .append("\t\tThe default is -1.").append(newLine + newLine).append("\t-g, --regex-file-copy-filter").append(newLine)
            .append("\t\tTreat the file name filter supplied with -n as a regular expression. The").append(newLine)
            .append("\t\tdefault is false.").append(newLine + newLine)
            .append("\t-G, --gmt").append(newLine)
            .append("\t\tConvert the date in the rename format string to Greenwich Mean Time.").append(newLine + newLine)
            .append("\t-h, --regex-dir-flatten-filter").append(newLine)
            .append("\t\tTreat the directory name flatten filter supplied with -D as a regular expression.").append(newLine)
            .append("\t\tThe default is false.").append(newLine + newLine)
            .append("\t-H, --case-sensitive-dir-flatten-filter").append(newLine)
            .append("\t\tTreat the directory name flatten filter supplied with -D as case sensitive.").append(newLine)
            .append("\t\tThe default is false.").append(newLine + newLine)
            .append("\t-i, --regex-dir-merge-filter").append(newLine)
            .append("\t\tTreat the directory name merge filter supplied with -N as a regular expression.").append(newLine)
            .append("\t\tThe default is false.").append(newLine + newLine)
            .append("\t-I, --case-sensitive-dir-merge-filter").append(newLine)
            .append("\t\tTreat the directory name merge filter supplied with -N as case sensitive.").append(newLine)
            .append("\t\tThe default is false.").append(newLine + newLine)
            .append("\t-j, --regex-dir-copy-filter").append(newLine)
            .append("\t\tTreat the directory name copy filter supplied with -d as a regular expression.").append(newLine)
            .append("\t\tThe default is false.").append(newLine + newLine)
            .append("\t-k, --exclusive-dir-flatten-filter").append(newLine)
            .append("\t\tTreat the directory name flatten filter supplied with -D as exclusive.").append(newLine)
            .append("\t\tThe default is false.").append(newLine + newLine)
            .append("\t-K, --exclusive-dir-merge-filter").append(newLine)
            .append("\t\tTreat the directory name merge filter supplied with -N as exclusive.").append(newLine)
            .append("\t\tThe default is false.").append(newLine + newLine)
            .append("\t-l, --log [DEFAULT, INFO, WARN, ERROR]").append(newLine)
            .append("\t\tThe level at which to set the logger. The default is INFO. Only the file").append(newLine)
            .append("\t\tlogger can be set to WARN or ERROR.").append(newLine + newLine)
            .append("\t-m, --max-dir-merge-level <integer value>").append(newLine)
            .append("\t\tThe maximum level of directories to merge. If the value is -1, no").append(newLine)
            .append("\t\tdirectories are merged. If the value is 0, all directories are merged.").append(newLine)
            .append("\t\tThe default is -1.").append(newLine + newLine)
            .append("\t-n, --file-name-copy-filter <filter>").append(newLine)
            .append("\t\tA filter for files. Files whose name matches the filter will or will not").append(newLine)
            .append("\t\tbe copied, depending on if it is inclusive or exclusive.").append(newLine + newLine)
            .append("\t-N, --dir-name-merge-filter <filter>").append(newLine)
            .append("\t\tA filter for directories. Directories whose name matches the filter will").append(newLine)
            .append("\t\tor will not be merged, depending on if it is inclusive or exclusive.").append(newLine + newLine)
            .append("\t-o, --set-copy-path <copy path>").append(newLine)
            .append("\t\tSet the copy path to the path or list of paths that are entered. The").append(newLine)
            .append("\t\tprevious copy path is overwritten.").append(newLine + newLine)
            .append("\t-O, --get-copy-path").append(newLine)
            .append("\t\tView the copy path. This option is ignored if started in GUI mode.").append(newLine + newLine)
            .append("\t-p, --prompt-restore").append(newLine)
            .append("\t\tPrompt before restoring each file when restoring files.").append(newLine + newLine)
            .append("\t-q, --set-restore-path <restore path>").append(newLine)
            .append("\t\tSet the restore path to the path or list of paths that are entered. The").append(newLine)
            .append("\t\tprevious restore path is overwritten.").append(newLine + newLine)
            .append("\t-Q, --get-restore-path").append(newLine)
            .append("\t\tView the restore path. This option is ignored if started in GUI mode.").append(newLine + newLine)
            .append("\t-r, --restore-script  <restore script file>").append(newLine)
            .append("\t\tThe restore script file that contains the full path of the files to").append(newLine)
            .append("\t\trestore. The name only of the from script file can be entered if the script").append(newLine)
            .append("\t\tfile exists in the Restore Path. This argument is reguired in console mode").append(newLine)
            .append("\t\t (-c) to restore files.").append(newLine + newLine)
            .append("\t-R, --no-restore").append(newLine)
            .append("\t\tDo not build a restore script when copying files. The default is false.").append(newLine + newLine)
            .append("\t-s, --case-sensitive-file-copy-filter").append(newLine)
            .append("\t\tThe file name filter supplied with -n is case-sensitive. The default is").append(newLine)
            .append("\t\tfalse.").append(newLine + newLine)
            .append("\t-S, --case-sensitive-dir-copy-filter").append(newLine)
            .append("\t\tThe directory name filter supplied with -d is case-sensitive. The default is").append(newLine)
            .append("\t\tcase-insensitive.").append(newLine + newLine)
            .append("\t-t, --to-file").append(newLine)
            .append("\t\tThe copy script file that contains the full path of the directories to").append(newLine)
            .append("\t\twhich to copy. The name only of the to script file can be entered if the").append(newLine)
            .append("\t\tscript file exists in the Copy Path. This argument is reguired in console").append(newLine)
            .append("\t\tmode (-c) to copy files.").append(newLine + newLine)
            .append("\t-u, --update").append(newLine)
            .append("\t\tCopy files only if the origin file is newer than the destination file.").append(newLine + newLine)
            .append("\t\t-v, --version").append(newLine)
            .append("\t\tView the application version. This option is ignored if started in GUI mode.").append(newLine+newLine)
            .append("\t-w, --prompt-overwrite").append(newLine)
            .append("\t\tPrompt before overwriting a file.").append(newLine + newLine)
            .append("\t-x, --exclusive-file-copy-filter").append(newLine)
            .append("\t\tTreat the file filter as an exclusive filter. The default is inclusive.").append(newLine + newLine)
            .append("\t-X, --exclusive-dir-copy-filter").append(newLine)
            .append("\t\tTreat the directory filter as an exclusive filter. The default is inclusive.").append(newLine + newLine)
            .append("\t-y, --max-dir-copy-level <integer value>").append(newLine)
            .append("\t\tThe maximum level of directories to copy. 0 means copy all directories").append(newLine)
            .append("\t\tThe default is 0.").append(newLine + newLine);

        logger.error(usageMsg.toString());
        return usageMsg.toString();
    }
    
    private void usage() {
        InputStream is = FileCopier.class.getResourceAsStream("../usage.txt");
        if(is != null) {
            InputStreamReader isr = new InputStreamReader(is);
            BufferedReader br = new BufferedReader(isr);
            try {
                StringBuilder sb = new StringBuilder();
                String line = null;
                String sep = System.getProperty("line.separator");
                while((line = br.readLine()) != null) {
                    sb.append(line + sep);
                }
                logger.error(sb);
            } catch(Exception e) {
                logger.error(usageBkup());
            } finally {
                try {
                    is.close();
                    isr.close();
                    br.close();
                } catch(IOException e){}
            }
        }
    }
}
